package org.sean.framework.util;

import lombok.extern.slf4j.Slf4j;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Url 工具
 *
 * @author xielei
 */
@Slf4j
public class UrlUtil {

    private static final String URL_CODING = "utf-8";
    private static final String IP_PATTERN = "(\\d){1,3}\\.(\\d){1,3}" + "\\.(\\d){1,3}\\.(\\d){1,3}(:\\d{1,4})?(/(.*))?";
    private static final Pattern VALID_URL = Pattern.compile("(.+)(\\.)(.+)[^\\w]*(.*)", Pattern.CASE_INSENSITIVE);
    private static final Pattern VALID_LOCAL_URL = Pattern.compile("(^file://.+)|(.+localhost:?\\d*/?.*)", Pattern.CASE_INSENSITIVE);
    private static final Pattern VALID_MTT_URL = Pattern.compile("mtt://(.+)", Pattern.CASE_INSENSITIVE);
    private static final Pattern VALID_QB_URL = Pattern.compile("qb://(.+)", Pattern.CASE_INSENSITIVE);
    private static final Pattern VALID_PAY_URL = Pattern.compile("(tenpay|alipay)://(.+)", Pattern.CASE_INSENSITIVE);
    private static final Pattern VALID_IP_ADDRESS = Pattern.compile(IP_PATTERN, Pattern.CASE_INSENSITIVE);
    private static final Pattern VALID_WEB_URL = Pattern.compile("(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]");

    private UrlUtil() {
    }

    /**
     * 处理url中的中文字符
     *
     * @param url url
     * @return encoder chinese char
     */
    public static String escapeAllChinese(String url) {
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < url.length(); i++) {
            char c = url.charAt(i);

            if (c >= 0x4E00 && c <= 0x9FFF || c >= 0xFE30 && c <= 0xFFA0) {
                String escapedStr;
                try {
                    escapedStr = URLEncoder.encode(String.valueOf(c), URL_CODING);

                    sb.append(escapedStr);
                } catch (UnsupportedEncodingException e) {
                    log.warn(e.getMessage(), e);
                }
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }

    /**
     * 是否javaScript
     *
     * @param url url
     * @return 是否javaScript
     */
    public static boolean isJavascript(String url) {
        // javascript:
        return (null != url) && (url.length() > 10)
                && "javascript:".equalsIgnoreCase(url.substring(0, 11));
    }

    /**
     * 特殊url
     *
     * @param url url
     * @return 是否特殊url
     */
    public static boolean isSpecialUrl(String url) {
        if (url == null) {
            return false;
        }

        String lowser = url.toLowerCase();
        return lowser.startsWith("about:blank")
                || lowser.startsWith("data:");
    }

    /**
     * 判断URL是否是一个有效的格式
     *
     * @param url url
     * @return 是否合法
     */
    public static boolean isValidUrl(String url) {
        if (url == null || url.length() == 0 || url.startsWith("data:")) {
            return false;
        }
        url = url.trim();

        Matcher validUrl = VALID_URL.matcher(url);
        Matcher validLocal = VALID_LOCAL_URL.matcher(url);
        Matcher validIp = VALID_IP_ADDRESS.matcher(url);
        Matcher validMtt = VALID_MTT_URL.matcher(url);
        Matcher validQb = VALID_QB_URL.matcher(url);
        Matcher validPay = VALID_PAY_URL.matcher(url);

        return validUrl.find() || validLocal.find() || validIp.find() || validMtt.find()
                || validQb.find() || validPay.find();
    }

    /**
     * 判断URL是否有一个有效的协议头
     *
     * @param url url
     * @return 是否合法
     */
    public static boolean isValidProtocal(final String url) {
        if (url == null || url.length() == 0) {
            return false;
        }

        // 2013-06-29, modified by p_edenwang
        // Remove trim and toLowerCase operation for url.
        // Spaces and uppercase will not affect the order of "://" & "."
        // This modification will save 20+ms when loading www.sohu.com/?fr=wap.
        int pos1 = url.indexOf("://");
        int pos2 = url.indexOf('.');

        // 检测"wap.fchgame.com/2/read.jsp?url=http://www.zaobao.com/zg/zg.shtml"类型网址
        if (pos1 > 0 && pos2 > 0 && pos1 > pos2) {
            return false;
        }

        return url.contains("://");
    }

    /**
     * 根据输入，得到一个有效URL 如果输入无法被解析为一个URL，返回NULL
     *
     * @param url url
     * @return 有效url
     */
    public static String resolveValidUrl(String url) {
        if (url == null || url.length() == 0) {
            return null;
        }

        url = url.trim();

        if (isJavascript(url) || UrlUtil.isSpecialUrl(url)) {
            return url;
        } else if (isValidUrl(url)) {
            if (isValidProtocal(url)) {
                return url;
            } else {
                return "http://" + url;
            }
        } else {
            return null;
        }
    }

    /**
     * Url 连接 子路径
     *
     * @param pre  原始路径
     * @param part 子路径
     * @return 拼接后路径
     */
    public static String contatPart(String pre, String part) {
        if (StringUtil.isBlank(pre)) {
            return pre;
        }
        if (StringUtil.isBlank(part)) {
            return pre;
        }
        if (pre.endsWith("/")) {
            return pre + (part.startsWith("/") ? part.replaceFirst("^/+", "") : part);
        } else {
            return pre + "/" + (part.startsWith("/") ? part.replaceFirst("^/+", "") : part);
        }
    }


    /**
     * Url 连接 Query
     *
     * @param url   url
     * @param query 参数
     * @return 拼接后url
     */
    public static String contatQuery(String url, String query) {
        if (StringUtil.isBlank(url)) {
            return url;
        }
        if (StringUtil.isBlank(query)) {
            return url;
        }
        if (url.endsWith("?")) {
            return url + (query.startsWith("?") ? query.replaceFirst("^\\?+", "") : query);
        } else {
            return url + "?" + (query.startsWith("?") ? query.replaceFirst("^\\?+", "") : query);
        }
    }


    /**
     * url 添加参数
     *
     * @param url        原始URL
     * @param paramName  参数名
     * @param paramValue 参数值
     * @return 拼接后URL
     */
    public static String addURLParam(String url, String paramName, String paramValue) {
        //参数和参数名为空的话就返回原来的URL
        if (StringUtil.isEmpty(paramValue) || StringUtil.isEmpty(paramName)) {
            return url;
        }
        //先很据# ? 将URL拆分成一个String数组
        String a = "";
        String b = "";
        String c = "";

        String[] abcArray = url.split("[?]");
        a = abcArray[0];
        if (abcArray.length > 1) {
            String bc = abcArray[1];
            String[] bcArray = bc.split("#");
            b = bcArray[0];
            if (bcArray.length > 1) {
                c = bcArray[1];
            }
        }
        if (StringUtil.isEmpty(b)) {
            return url + "?" + urlDecode(paramName) + "=" + urlDecode(paramValue);
        }

        // 用&拆分参数, p1=1&p2=2 ，{p1=1,p2=2}
        String[] parts = b.split("&");
        StringBuilder newb = new StringBuilder();
        boolean found = false;
        for (String part : parts) {
            if (StringUtil.isEmpty(part)) {
                continue;
            }
            String key = "";
            String value = "";

            String[] kv = part.split("=");
            key = kv[0];
            if (kv.length > 1) {
                value = kv[1];
            }

            if (key.equals(paramName)) {
                found = true;
                if (!StringUtil.isEmpty(paramValue)) {
                    newb.append("&").append(key).append("=").append(paramValue);
                }
            } else {
                newb.append("&").append(key).append("=").append(value);
            }
        }
        // 如果没找到，加上
        if (!found && !StringUtil.isEmpty(paramValue)) {
            newb.append("&").append(paramName).append("=").append(paramValue);
        }
        if (!StringUtil.isEmpty(newb.toString())) {
            a = a + "?" + newb.substring(1);
        }
        if (!StringUtil.isEmpty(c)) {
            a = a + "#" + c;
        }
        return a;
    }

    /**
     * UTF-8 编码
     *
     * @param url url
     * @return 编码后url
     */
    public static String urlDecode(String url) {
        try {
            return URLDecoder.decode(url, StandardCharsets.UTF_8.name());
        } catch (Exception e) {
            return url;
        }
    }

    /**
     * 提取url
     *
     * @param value 文本
     * @return 提取url列表
     */
    public static List<String> extractUrls(String value) {
        if (StringUtil.isBlank(value)) {
            return Collections.emptyList();
        }
        List<String> urls = new ArrayList<>();
        Matcher matcher = VALID_WEB_URL.matcher(value);
        while (matcher.find()) {
            urls.add(matcher.group());
        }
        return urls;
    }

    /**
     * 组装URL
     *
     * @param host host
     * @param port port
     * @return url
     */
    public static String getHttpUrl(String host, int port) {
        return getUrl("http", host, port);
    }

    /**
     * 组装URL
     *
     * @param host host
     * @param port port
     * @return url
     */
    public static String getHttpsUrl(String host, int port) {
        return getUrl("http", host, port);
    }

    /**
     * 组装URL
     *
     * @param schema schema
     * @param host   host
     * @param port   port
     * @return url
     */
    public static String getUrl(String schema, String host, int port) {
        return StringUtil.formatContent("{0}://{1}:{2,number,#}", schema, host, port);
    }
}
